/**
  ******************************************************************************
  * @file    bsp.c
  * @author  Iron
  * @date    2018-08-03
  * @version v1.0
  * @brief   bsp c file
  */

/** @addtogroup GROUP_BSP
  * @{
  */

/* includes ------------------------------------------------------------------*/
#include <time.h>
#include <string.h>
#include "stm32f4xx_hal.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "timers.h"
#include "bsp.h"
#include "bsp_led.h"
#include "bsp_rtc.h"
#include "bsp_usart_ex.h"
#include "bsp_watchdog.h"

// DEBUG
#include "debug_log.h"
#define LOG_LOCAL_LEVEL DBG_LOG_DEBUG
DBG_LOG_TAG("BSP");

/* private typedef -----------------------------------------------------------*/
/* private macro -------------------------------------------------------------*/
#define BSP_BSP_RTOS_ENTER_CRITICAL()  portENTER_CRITICAL()
#define BSP_BSP_RTOS_EXIT_CRITICAL()   portEXIT_CRITICAL()

#define BSP_LOG_USART BSP_USART1

/* private variables ---------------------------------------------------------*/
/* private function prototypes -----------------------------------------------*/
/* private functions ---------------------------------------------------------*/

/**
  * @brief  stm32f4xx_hal_msp This function is executed in case of error occurrence.
  * @param  file: The file name as string.
  * @param  line: The line in file as a number.
  * @retval None
  */
void _Error_Handler(char *file, int line)
{
    /* User can add his own implementation to report the HAL error return state */
    DBG_LOGE("Error_Handler", "Fatal error at FILE: %s line: %d", file, line);
    while (1) {}
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct;
    RCC_ClkInitTypeDef RCC_ClkInitStruct;
    RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;

    /**Configure the main internal regulator output voltage
    */
    __HAL_RCC_PWR_CLK_ENABLE();

    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

    /**Initializes the CPU, AHB and APB busses clocks
    */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE | RCC_OSCILLATORTYPE_LSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.LSEState = RCC_LSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 4;
    RCC_OscInitStruct.PLL.PLLN = 168;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
    RCC_OscInitStruct.PLL.PLLQ = 4;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }

    /**Initializes the CPU, AHB and APB busses clocks
    */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                  | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }

    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
    PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }

    /**Configure the Systick interrupt time
    */
    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000);

    /**Configure the Systick
    */
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

    /* SysTick_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0);
}

/**
  * Initializes the Global MSP.
  */
void HAL_MspInit(void)
{
    __HAL_RCC_SYSCFG_CLK_ENABLE();
    __HAL_RCC_PWR_CLK_ENABLE();

    HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

    /* System interrupt init*/
    /* MemoryManagement_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(MemoryManagement_IRQn, 0, 0);
    /* BusFault_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(BusFault_IRQn, 0, 0);
    /* UsageFault_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(UsageFault_IRQn, 0, 0);
    /* SVCall_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(SVCall_IRQn, 0, 0);
    /* DebugMonitor_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(DebugMonitor_IRQn, 0, 0);
    /* PendSV_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(PendSV_IRQn, 15, 0);
    /* SysTick_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0);
}

/**
  * @brief  Period elapsed callback in non blocking mode
  * @note   This function is called  when TIM1 interrupt took place, inside
  * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
  * a global variable "uwTick" used as application time base.
  * @param  htim : TIM handle
  * @retval None
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM1)
    {
        HAL_IncTick();
    }
}

static void MX_GPIO_Init(void)
{
    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    __HAL_RCC_GPIOE_CLK_ENABLE();
    __HAL_RCC_GPIOH_CLK_ENABLE();
	__HAL_RCC_GPIOG_CLK_ENABLE();
}

/**
  * @brief bsp log usart
    */
#ifdef BSP_CLI_ENABLE
static StaticSemaphore_t usart1_rx_sem;
static SemaphoreHandle_t usart1_rx_sem_handle;

// bsp_usart.c bsp_usart_irq->_bsp_usart_rx_isr
void bsp_log_usart_rx_indicate_cb(void *handle, uint32_t length)
{
    if (usart1_rx_sem_handle)
    {
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
        xSemaphoreGiveFromISR(usart1_rx_sem_handle, &xHigherPriorityTaskWoken);
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
}

int32_t bsp_getc(void)
{
    int32_t res;
    uint8_t data;

    do
    {
        res = bsp_usart_rx(BSP_USART1, &data, 1);
        if (res != 1)
        {
            xSemaphoreTake(usart1_rx_sem_handle, portMAX_DELAY);
        }
    }
    while (res != 1);

    return data;
}

void bsp_putc(char ch)
{
    bsp_usart_putc(BSP_LOG_USART, ch);
}

int32_t bsp_puts(const char *s)
{
    return bsp_usart_poll_tx(BSP_LOG_USART, (uint8_t *)s, strlen(s), 500);
}

#endif

/**
 * @brief This function will delay for some us.
 * @param us - the delay time of us.
 */
void bsp_us_delay(uint32_t us)
{
    uint32_t delta;

    us = us * (SysTick->LOAD / (1000000 / configTICK_RATE_HZ));
    delta = SysTick->VAL;

    if (delta < us)
    {
        while (SysTick->VAL < us);
        us -= delta;
        delta = SysTick->LOAD;
    }

    while ((delta - SysTick->VAL) < us);
}

void bsp_os_delay(uint32_t ms)
{
    vTaskDelay(pdMS_TO_TICKS(ms));
}

uint32_t bsp_get_time_ms(void)
{
    return xTaskGetTickCount() * portTICK_PERIOD_MS;
}

void bsp_print_rtc_time(void)
{
    time_t now_time;
    struct tm tm_time;

    memset(&tm_time, 0, sizeof(tm_time));
    bsp_get_rtc_time(&tm_time, &now_time, NULL);

    DBG_LOGD(TAG, "Device boot up at time: UTC %d/%02d/%02d %02d:%02d:%02d [ %d ]",
             tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, now_time);
}

/**
  * @breaf bsp_system_reboot
  */
static TimerHandle_t bsp_system_reboot_tmrId;
static StaticTimer_t bsp_system_reboot_tmr;

static void system_reboot_timer_cb(TimerHandle_t xExpiredTimer)
{
    BSP_BSP_RTOS_ENTER_CRITICAL();
    HAL_NVIC_SystemReset();
// BSP_BSP_RTOS_EXIT_CRITICAL();
}

static void bsp_system_reboot_tmr_init(void)
{
    bsp_system_reboot_tmrId = xTimerCreateStatic("reboot", portMAX_DELAY, pdTRUE, NULL, system_reboot_timer_cb, &bsp_system_reboot_tmr);
}

void bsp_system_reboot(uint32_t delay_ms)
{
    xTimerStop(bsp_system_reboot_tmrId, 0);
    xTimerChangePeriod(bsp_system_reboot_tmrId, pdMS_TO_TICKS(delay_ms), 0);
    xTimerStart(bsp_system_reboot_tmrId, 0);
}

/**
  * @brief bsp_start_info
    */
void bsp_start_info(void)
{
    uint32_t flag;

    flag = __HAL_RCC_GET_FLAG(RCC_FLAG_BORRST);
    if (flag)
    {
        bsp_puts("POR/PDR or BOR Reset Falg is SET\r\n");
    }

    flag = __HAL_RCC_GET_FLAG(RCC_FLAG_PORRST);
    if (flag)
    {
        bsp_puts("POR/PDR Reset Falg is SET\r\n");
    }

    flag = __HAL_RCC_GET_FLAG(RCC_FLAG_PINRST);
    if (flag)
    {
        bsp_puts("Pin Reset Falg is SET\r\n");
    }

    flag = __HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST);
    if (flag)
    {
        bsp_puts("Software Reset Falg is SET\r\n");
    }

    flag = __HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST);
    if (flag)
    {
        bsp_puts("Independent Watchdog Reset Falg is SET\r\n");
    }

    flag = __HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST);
    if (flag)
    {
        bsp_puts("Window Watchdog Reset Falg is SET\r\n");
    }

    flag = __HAL_RCC_GET_FLAG(RCC_FLAG_LPWRRST);
    if (flag)
    {
        bsp_puts("Low Power Reset Falg is SET\r\n");
    }

    flag = *(__IO uint32_t *)0x1FFFC000;
    DBG_LOGD(TAG, "Flash RDP: 0x%02X, nRST_STDBY: %d, nRST_STOP: %d, WDG_SW: %d, BOR_LEV: %d",
             (flag >> 8) & 0xFF, (flag >> 7) & 0x01, (flag >> 6) & 0x01, (flag >> 5) & 0x01, (flag >> 2) & 0x03);
    flag = *(__IO uint32_t *)0x1FFFC008;
    DBG_LOGD(TAG, "Flash sector WRP: 0x%04X", flag); // 0: enable 1: disable

#ifdef APP_RELEASE_VERSION
    // TODO: Enable Flash Read Write Protect.
#endif

    __HAL_RCC_CLEAR_RESET_FLAGS();
    __HAL_RCC_CCMDATARAMEN_CLK_ENABLE();    // CCM SRAM CLOCK
//__HAL_RCC_BKPSRAM_CLK_DISABLE();        // 4K BKP SRAM

    // VECT_TAB_OFFSET
    DBG_LOGD(TAG, "Applcaiton Vector Table Address: 0x%08X", SCB->VTOR);
}

/**
  * @brief  Init the low level hardware
  * @param  None.
  * @retval OK: normally return;
  *         ERROR: error;
  */
void bsp_init(void)
{
    MX_GPIO_Init();

    /* system reboot timer init */
    bsp_system_reboot_tmr_init();

    /* debug usart init */
#ifdef BSP_CLI_ENABLE
    usart1_rx_sem_handle = xSemaphoreCreateBinaryStatic(&usart1_rx_sem);
    bsp_usart_init(NULL, BSP_LOG_USART, 115200, bsp_log_usart_rx_indicate_cb, NULL);
#endif

    /* watchdog */
    bsp_watchdog_init();
    bsp_watchdog_keepalive();

    /* rtc init */
    bsp_rtc_init();

#ifdef BSP_CLI_ENABLE
    bsp_puts("\r\n");
    bsp_puts("*******************************************\r\n");
    bsp_puts("*** STM32F4xx FreeRTOS                  ***\r\n");
    bsp_puts("*******************************************\r\n");

    bsp_puts("\r\nDevice Boot Up. < Program Complied at " __DATE__ " " __TIME__ " >\r\n");

    bsp_start_info();

    bsp_print_rtc_time();
#endif

    bsp_watchdog_keepalive(); // usually time consume 45ms
}


/**
  * @}
  */

/******************* (C)COPYRIGHT 2018 ***** END OF FILE *********************/
